/* This software was developed at the National Institute of Standards and
 * Technology by employees of the Federal Government in the course of
 * their official duties. Pursuant to title 17 Section 105 of the United
 * States Code this software is not subject to copyright protection and
 * is in the public domain.
 * NIST assumes no responsibility whatsoever for its use by other parties,
 * and makes no guarantees, expressed or implied, about its quality,
 * reliability, or any other characteristic.
 * <BR>
 * We would appreciate acknowledgement if the software is used.
 * <BR>
 * NIST ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND
 * DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING
 * FROM THE USE OF THIS SOFTWARE.
 * </PRE></P>
 * @author  ksshar
 */
#include "arqstatus.h"
#include "packet.h"
#include "mac802_16.h"

/* Maximum Sequence number for ARQ*/
#define MAX_SEQ 2048

/*
 * Create an Arq Status object
 * @param
 */
Arqstatus::Arqstatus ():arq_retrans_time_ (0), ack_seq_ (0), ack_counter_ (0),
        arq_curr_seq_ (0), ack_period_ (0), arq_max_seq_ (0),
        arq_max_window_ (0), arq_curr_window_ (0), last_ack_sent_ (0)
{
    arq_trans_queue_ = new PacketQueue;
    arq_retrans_queue_ = new PacketQueue;
    arq_feedback_queue_ = new PacketQueue;
    arq_max_seq_ = MAX_SEQ;

}

/*
 * Create an Arq Status object
 * @param
 */
Arqstatus::~Arqstatus ()
{
    if (arq_trans_queue_)
        delete arq_trans_queue_;
    if (arq_retrans_queue_)
        delete arq_retrans_queue_;
    if (arq_feedback_queue_)
        delete arq_feedback_queue_;

    delete arqRetransTimer;
    /*RPI*/
}

void
Arqstatus::arqTimerHandler ()
{
    Packet *ph, *p, *pac;
    bool dont_queue = false, dont_queue_retrans = false;
    Connection *connection = arqRetransTimer->getConnection ();
    debug2 ("ARQ Timer Expire for Connection Id: %d \n",
            connection->get_cid ());
    arq_trans_queue_->resetIterator ();
    // send all packets with active timers to retransmission queue
    ph = arq_trans_queue_->getNext ();

    debug2 ("  ARQ: con %d qlength=%d arqTx=%d arqRtx=%d\n",
            connection->get_cid (), connection->get_queue ()->length (),
            arq_trans_queue_->length (), arq_retrans_queue_->length ());

    while (ph)
    {
        connection->get_queue ()->resetIterator ();
        p = connection->get_queue ()->getNext ();
        while (p)
        {
            if (connection->isPackingEnable ())
            {
                if (HDR_MAC802_16 (p)->pack_subheader.sn ==
                        HDR_MAC802_16 (ph)->pack_subheader.sn)
                {
                    debug2
                    ("  ARQ Timer handler, NOT Enqueueing packet in Retransmission queue Seq No : %d\n ",
                     HDR_MAC802_16 (p)->pack_subheader.sn);
                    dont_queue = true;
                    break;
                }
            }
            else
            {
                if (HDR_MAC802_16 (p)->frag_subheader.sn ==
                        HDR_MAC802_16 (ph)->frag_subheader.sn)
                {
                    dont_queue = true;
                    break;
                }
            }
            p = connection->get_queue ()->getNext ();
        }

        arq_retrans_queue_->resetIterator ();
        pac = arq_retrans_queue_->getNext ();
        while (pac)
        {
            if (connection->isPackingEnable ())
            {
                if (HDR_MAC802_16 (pac)->pack_subheader.sn ==
                        HDR_MAC802_16 (ph)->pack_subheader.sn)
                {
                    debug2
                    ("  ARQ Timer handler, NOT Enqueueing packet in Retransmission queue, already in Retrans queue Seq No : %d\n ",
                     HDR_MAC802_16 (pac)->pack_subheader.sn);
                    dont_queue_retrans = true;
                    break;
                }
            }
            else
            {
                if (HDR_MAC802_16 (pac)->frag_subheader.sn ==
                        HDR_MAC802_16 (ph)->frag_subheader.sn)
                {
                    dont_queue_retrans = true;
                    break;
                }
            }
            pac = arq_retrans_queue_->getNext ();
        }

        if ((!dont_queue) && (!dont_queue_retrans))
        {
            debug2
            ("  ARQ Timer handler, Enqueueing packet in Retransmission queue Seq No : %d \n",
             HDR_MAC802_16 (ph)->pack_subheader.sn);
            arq_retrans_queue_->enque (ph->copy ());
        }
        dont_queue = false;
        dont_queue_retrans = false;
        ph = arq_trans_queue_->getNext ();
    }
    //schedule timer based on retransmission time setting
    debug2 ("  ARQ: con %d qlength=%d arqTx=%d arqRtx=%d\n",
            connection->get_cid (), connection->get_queue ()->length (),
            arq_trans_queue_->length (), arq_retrans_queue_->length ());
    arqRetransTimer->resched (arq_retrans_time_);
    debug2 ("  ARQ Timer Re-Set\n");
}

void
Arqstatus::arqSend (Packet * p, Connection * connection,
                    fragment_status status)
{
    hdr_mac802_16 *mh = HDR_MAC802_16 (p);
    int cid = connection->get_cid ();
    // If Packing is enabled the ARQ and Fragmentation information is contained in the packing subheader
    if (connection->isPackingEnable () == true)
    {
        mh->pack_subheader.sn = arq_curr_seq_;
        mh->pack_subheader.fc = status;
        debug2
        ("ARQ Send in packing subheader: cid:%d curr  seq:%d fragment_status : %d\n",
         cid, mh->pack_subheader.sn, status);
    }
    else
    {
        mh->frag_subheader.sn = arq_curr_seq_;
        mh->frag_subheader.fc = status;
        debug2
        ("ARQ Send in fragmentation subheader: cid:%d curr  seq:%d fragment_status : %d\n",
         cid, mh->frag_subheader.sn, status);
    }
    arq_curr_seq_++;
    if (arq_curr_seq_ == arq_max_seq_)
    {
        arq_curr_seq_ = 0;
    }
    // Add packet to tranmitted queue
    arq_trans_queue_->enque (p->copy ());
    debug2 ("ARQ Send: added to  ARQ Transmission queue, length: %d \n",
            arq_trans_queue_->length ());
}


void
Arqstatus::arqReceive (Packet * p, Connection * connection,
                       u_int8_t * inOrder)
{

    // Read the header to find out the flow the packet belongs to
    hdr_mac802_16 *mh = HDR_MAC802_16 (p);
    u_int32_t flowno = mh->header.cid;
    u_int32_t seqno;
    if (connection->isPackingEnable () == true)
    {
        seqno = mh->pack_subheader.sn;
    }
    else
    {
        seqno = mh->frag_subheader.sn;
    }
    debug2
    ("ARQ: Receive: ack_seq_ = %d ; seqno = %d ; ack_counter_ = %d ; ack_period_ = %d\n",
     ack_seq_, seqno, ack_counter_, ack_period_);
    // Read sequence number and check for in order packet
    // if out of order, free the packet and reset inOrder variable
    if (ack_seq_ != seqno)
    {
        //if((((seqno - ack_seq_) > 0) && ((seqno - ack_seq_) <= arq_max_window_))
        //|| ((seqno >= 0 && seqno < arq_max_window_) && (ack_seq_ < MAX_SEQ && ack_seq_ >= (MAX_SEQ - arq_max_window_))))
        if ((((seqno - ack_seq_) > 0)
                && ((seqno - ack_seq_) <= arq_max_window_))
                || ((seqno - ack_seq_ < 0)
                    && (seqno + MAX_SEQ - ack_seq_ <= arq_max_window_)))
        {
            *inOrder = 0;
            debug2
            ("ARQ receive: out of order - in arq receive ack_seq_ : %d seq_no. in packet : %d\n",
             ack_seq_, seqno);
        }
        else
        {
            *inOrder = 2;
            debug2
            ("ARQ receive: No need to buffer packet - in arq receive ack_seq_ : %d seq_no. in packet : %d\n",
             ack_seq_, seqno);
        }
    }
    // if in order, increment the feedback sequence number
    if (ack_seq_ == seqno)
    {
        ack_seq_++;
        if (ack_seq_ == arq_max_seq_)
        {
            ack_seq_ = 0;
        }
        debug2 ("ARQ Receive: in order: flow:%d \t seq no: %d\t ack_seq=%d\n",
                flowno, seqno, ack_seq_);
    }
    //Begin RPI
    // increment the ACK counter and check if time to create and enque feedback IE
    ack_counter_++;
    if (ack_counter_ == ack_period_)  				// if time to send ACK
    {
        ack_counter_ = 0;
        if ((*inOrder == 1))  			//Case of Cumulative acknowledgement
        {
            //if((((ack_seq_ - seqno) > 0) && ((ack_seq_ -  seqno) <= arq_max_window_)) || ((ack_seq_ >= 0 && ack_seq_ < arq_max_window_) && (seqno < MAX_SEQ && seqno >= (MAX_SEQ - arq_max_window_))))
            if ((((ack_seq_ - seqno) > 0)
                    && ((ack_seq_ - seqno) <= arq_max_window_))
                    || ((ack_seq_ - seqno < 0)
                        && (ack_seq_ + MAX_SEQ - seqno <= arq_max_window_)))
            {
                debug2
                ("ARQ Receive: Enqueueing a cumulative acknowledgement \n");
                // Let the cumulative acknowledgement always occupy the first slot and here we will fill only ARQ IE information
                Packet *arqfb = arq_feedback_queue_->tail ();
                if (arqfb
                        && (HDR_MAC802_16 (arqfb)->num_of_acks <= MAX_NUM_ACK))
                {
                    arq_feedback_queue_->remove (arqfb);	// Remove the packet and queue it later
                    hdr_mac802_16 *gh = HDR_MAC802_16 (arqfb);
                    gh->arq_ie[0].cid = flowno;
                    gh->arq_ie[0].last = 0;	// this is the last IE
                    gh->arq_ie[0].ack_type = 1;	// cumulative ACK
                    gh->arq_ie[0].fsn = ack_seq_ - 1;	// sequence number in ACK
                    arq_feedback_queue_->enque (arqfb);
                    debug2
                    ("ARQ Receive: ACK queued, queue length: %d flow:%d fsn:%d\n",
                     arq_feedback_queue_->length (), flowno,
                     gh->arq_ie[0].fsn);
                }
                else
                {
                    Packet *arqfb = Packet::alloc ();
                    debug2 ("ARQ : Creating a new acknowledgement packet \n");
                    hdr_mac802_16 *gh = HDR_MAC802_16 (arqfb);
                    gh->arq_ie[0].cid = flowno;
                    gh->arq_ie[0].last = 0;	// this is the last IE
                    gh->arq_ie[0].ack_type = 1;	// cumulative ACK
                    gh->arq_ie[0].fsn = ack_seq_ - 1;	// sequence number in ACK
                    gh->num_of_acks = 1;	//
                    arq_feedback_queue_->enque (arqfb);
                    debug2
                    ("ARQ Receive: ACK queued, queue length: %d flow:%d fsn: %d \n",
                     arq_feedback_queue_->length (), flowno,
                     gh->arq_ie[0].fsn);
                }
                return;
            }
        }
        else if (*inOrder == 0)
        {
            last_ack_sent_ = seqno;	// set last ACK sent var to present ACK seq no
            debug2 ("ARQ Receive: Enqueueing a selective acknowledgement \n");
            Packet *arqfb = arq_feedback_queue_->tail ();
            if (arqfb && (HDR_MAC802_16 (arqfb)->num_of_acks <= MAX_NUM_ACK))
            {
                arq_feedback_queue_->remove (arqfb);	// Remove the packet and queue it later
                hdr_mac802_16 *gh = HDR_MAC802_16 (arqfb);
                int number_of_acks = gh->num_of_acks;
                gh->arq_ie[number_of_acks].cid = flowno;
                gh->arq_ie[number_of_acks].last = 0;	// this is the last IE
                gh->arq_ie[number_of_acks].ack_type = 2;	// Selective ACK
                gh->arq_ie[number_of_acks].fsn = last_ack_sent_;	// sequence number in ACK
                gh->num_of_acks = gh->num_of_acks + 1;
                arq_feedback_queue_->enque (arqfb);
                debug2 ("ARQ Receive: ACK queued, queue length: %d flow:%d\n",
                        arq_feedback_queue_->length (), flowno);
            }
            else
            {
                Packet *arqfb = Packet::alloc ();
                hdr_mac802_16 *gh = HDR_MAC802_16 (arqfb);
                gh->arq_ie[0].cid = flowno;
                gh->arq_ie[0].last = 0;	// this is the last IE
                gh->arq_ie[0].ack_type = 1;	// cumulative ACK
                gh->arq_ie[0].fsn = ack_seq_ - 1;	// sequence number in ACK
                gh->num_of_acks = 1;	//
                int number_of_acks = gh->num_of_acks;
                gh->arq_ie[number_of_acks].cid = flowno;
                gh->arq_ie[number_of_acks].last = 0;	// this is the last IE
                gh->arq_ie[number_of_acks].ack_type = 2;	// Selective ACK
                gh->arq_ie[number_of_acks].fsn = last_ack_sent_;	// sequence number in ACK
                gh->num_of_acks = gh->num_of_acks + 1;
                arq_feedback_queue_->enque (arqfb);
                debug2 ("ARQ Receive: ACK queued, queue length: %d flow:%d\n",
                        arq_feedback_queue_->length (), flowno);
            }
        }
    }
    debug2 ("Exiting ARQ Receive\n");
}

//End RPI

//Begin RPI
void
Arqstatus::arqReceiveBufferTransfer (Packet * p, Connection * connection,
                                     u_int8_t * inOrder)
{

    // Read the header to find out the flow the packet belongs to
    hdr_mac802_16 *mh = HDR_MAC802_16 (p);
    u_int32_t flowno = mh->header.cid;
    u_int32_t seqno;
    if (connection->isPackingEnable () == true)
    {
        seqno = mh->pack_subheader.sn;
    }
    else
    {
        seqno = mh->frag_subheader.sn;
    }
    debug2 ("ARQ cid=%d receive: type_arqfb:%d\n", flowno,
            mh->header.type_arqfb);
    debug2
    ("ack_seq_ = %d ; seqno = %d ; ack_counter_ = %d ; ack_period_ = %d\n",
     ack_seq_, seqno, ack_counter_, ack_period_);
    // Read sequence number and check for in order packet
    // if out of order, free the packet and reset inOrder variable
    if (ack_seq_ != seqno)
    {
        //if((((seqno - ack_seq_) > 0) && ((seqno - ack_seq_) <= arq_max_window_)) || ((seqno >= 0 && seqno < arq_max_window_) && (ack_seq_ < MAX_SEQ && ack_seq_ >= (MAX_SEQ - arq_max_window_))))
        if ((((seqno - ack_seq_) > 0)
                && ((seqno - ack_seq_) <= arq_max_window_))
                || ((seqno - ack_seq_ < 0)
                    && (seqno + MAX_SEQ - ack_seq_ <= arq_max_window_)))
        {
            *inOrder = 0;
            debug2
            ("ARQ cid=%d receive: out of order - in arq receive ack_seq_ : %d seq_no. in packet : %d\n",
             flowno, ack_seq_, seqno);
        }
        else
        {
            *inOrder = 2;
            debug2
            ("ARQ cid=%d receive: Should remove the packet to clean queue ack_seq_ : %d seq_no. in packet : %d\n",
             flowno, ack_seq_, seqno);
        }
    }
    // if in order, increment the feedback sequence number
    if (ack_seq_ == seqno)
    {
        ack_seq_++;
        if (ack_seq_ == arq_max_seq_)
        {
            ack_seq_ = 0;
        }
        debug2 ("ARQ cid=%d Receive: in order: flow:%d \t seq no: %d\n", flowno,
                flowno, seqno);
    }
    // increment the ACK counter and check if time to create and enque feedback IE
    ack_counter_++;
    if (ack_counter_ == ack_period_)  				// if time to send ACK
    {
        if ((*inOrder == 1))  			//Case of Cumulative acknowledgement
        {
            ack_counter_ = 0;
            //if((((ack_seq_ - seqno) > 0) && ((ack_seq_ -  seqno) <= arq_max_window_)) || ((ack_seq_ >= 0 && ack_seq_ < arq_max_window_) && (seqno < MAX_SEQ && seqno >= (MAX_SEQ - arq_max_window_))))
            if ((((ack_seq_ - seqno) > 0)
                    && ((ack_seq_ - seqno) <= arq_max_window_))
                    || ((ack_seq_ - seqno < 0)
                        && (ack_seq_ + MAX_SEQ - seqno <= arq_max_window_)))
            {
                debug2
                ("ARQ cid=%d Receive: Enqueueing a cumulative acknowledgement \n",
                 flowno);
                // Let the cumulative acknowledgement always occupy the first slot and here we will fill only ARQ IE information
                Packet *arqfb = arq_feedback_queue_->tail ();
                if (arqfb
                        && (HDR_MAC802_16 (arqfb)->num_of_acks <= MAX_NUM_ACK))
                {
                    arq_feedback_queue_->remove (arqfb);	// Remove the packet and queue it later
                    hdr_mac802_16 *gh = HDR_MAC802_16 (arqfb);
                    gh->arq_ie[0].cid = flowno;
                    gh->arq_ie[0].last = 0;	// this is the last IE
                    gh->arq_ie[0].ack_type = 1;	// cumulative ACK
                    gh->arq_ie[0].fsn = ack_seq_ - 1;	// sequence number in ACK
                    arq_feedback_queue_->enque (arqfb);
                    debug2
                    ("ARQ cid=%d Receive: ACK queued, queue length: %d flow:%d\n",
                     flowno, arq_feedback_queue_->length (), flowno);
                }
                else
                {
                    Packet *arqfb = Packet::alloc ();
                    hdr_mac802_16 *gh = HDR_MAC802_16 (arqfb);
                    gh->arq_ie[0].cid = flowno;
                    gh->arq_ie[0].last = 0;	// this is the last IE
                    gh->arq_ie[0].ack_type = 1;	// cumulative ACK
                    gh->arq_ie[0].fsn = ack_seq_ - 1;	// sequence number in ACK
                    gh->num_of_acks = 1;	//
                    arq_feedback_queue_->enque (arqfb);
                    debug2
                    ("ARQ cid=%d Receive: ACK queued, queue length: %d flow:%d\n",
                     flowno, arq_feedback_queue_->length (), flowno);
                }
                return;
            }
        }
        else
        {
            ack_counter_ = 0;	// set counter back to zero
        }
    }
    debug2 ("Exiting ARQ cid=%d Receive\n", flowno);
}

//End RPI
//Begin RPI
void
Arqstatus::arqRecvFeedback (Packet * p, u_int16_t i, Connection * connection)
{
    // Read the header to find the flow the feedback is for
    hdr_mac802_16 *mh = HDR_MAC802_16 (p);
    arq_fb_ie ie = mh->arq_ie[i];
    u_int16_t flowno = ie.cid;
    u_char ack_type = ie.ack_type;
    u_int32_t seq_recd = ie.fsn;
    u_int32_t sn;
    //debug2("ARQ Feedback: feedback recd for flow:%d and seq:%d ack_type : %d\n",flowno,seq_recd, ack_type);
    debug2
    ("  ARQ Feedback: feedback recd for con %d qlength=%d arqTx=%d arqRtx=%d\n",
     connection->get_cid (), connection->get_queue ()->length (),
     arq_trans_queue_->length (), arq_retrans_queue_->length ());

    if (ack_type == 1)
        ack_seq_ = seq_recd;
    // deque packets for which feedback received from transmission queue, free packet
    arq_trans_queue_->resetIterator ();
    while (arq_trans_queue_->length () != 0)
    {
        Packet *ph = (arq_trans_queue_)->getNext ();
        if (!ph)
        {
            // If one complete iteration is over, we can break the loop
            break;
        }
        hdr_mac802_16 *gh = HDR_MAC802_16 (ph);
        if (connection->isPackingEnable () == true)
        {
            sn = gh->pack_subheader.sn;
        }
        else
        {
            sn = gh->frag_subheader.sn;
        }
        debug2
        ("ARQ Feedback: packet has seq:%d\t and recd seq:%d (arq_curr_window_=%d, arq_max_window_=%d, MAX_SEQ=%d)\n",
         sn, seq_recd, arq_curr_window_, arq_max_window_, MAX_SEQ);
        //if ((ack_type == 1) && ((((seq_recd - sn) >= 0) && ((seq_recd - sn) < arq_max_window_)) || ((seq_recd >= 0 && seq_recd < arq_max_window_) && (sn < MAX_SEQ && (sn >= (MAX_SEQ - arq_max_window_)))))){
        if ((ack_type == 1) &&
                ((((seq_recd - sn) >= 0) && ((seq_recd - sn) < arq_max_window_))
                 || ((seq_recd - sn < 0)
                     && (seq_recd + MAX_SEQ - sn < arq_max_window_))))
        {
            arq_trans_queue_->remove (ph);
            Packet::free (ph);
            debug2 ("ARQ Feedback:dequeued1 packet with seq:%d\n", sn);
        }
        else if ((ack_type == 2) && (seq_recd == sn))
        {
            arq_trans_queue_->remove (ph);
            Packet::free (ph);
            debug2 ("ARQ Feedback:dequeued2 packet with seq:%d\n", sn);
        }
    }

    // deque packets for which feedback received from retransmission queue, free packet- This case can happen sometimes
    arq_retrans_queue_->resetIterator ();
    while (arq_retrans_queue_->length () != 0)
    {
        Packet *ph = (arq_retrans_queue_)->getNext ();
        if (!ph)
        {
            // If one complete iteration is over, we can break the loop
            break;
        }
        hdr_mac802_16 *gh = HDR_MAC802_16 (ph);
        if (connection->isPackingEnable () == true)
        {
            sn = gh->pack_subheader.sn;
        }
        else
        {
            sn = gh->frag_subheader.sn;
        }
        debug2 ("ARQ Feedback: packet has seq:%d\t and recd seq:%d\n", sn,
                seq_recd);
        //if ((ack_type == 1) && ((((seq_recd - sn) >= 0) && ((seq_recd - sn) < arq_max_window_))  || ((seq_recd >= 0 && seq_recd < arq_max_window_) && (sn < MAX_SEQ && (sn >= (MAX_SEQ - arq_max_window_)))))){
        if ((ack_type == 1) &&
                ((((seq_recd - sn) >= 0) && ((seq_recd - sn) < arq_max_window_))
                 || ((seq_recd - sn < 0)
                     && (seq_recd + MAX_SEQ - sn < arq_max_window_))))
        {
            arq_retrans_queue_->remove (ph);
            Packet::free (ph);
            debug2
            ("ARQ Feedback:dequeued packet from retrans queue with seq:%d\n",
             sn);
        }
        else if ((ack_type == 2) && (seq_recd == sn))
        {
            arq_retrans_queue_->remove (ph);
            Packet::free (ph);
            debug2
            ("ARQ Feedback:dequeued packet from retrans queue with seq:%d\n",
             sn);
        }
    }

}

// End RPI
